// 百度坐标系-bg-09、火星坐标系-gcj-02、wgs84、web墨卡托之间的互相转换-工具类

const ol = window.ol

const xPI = 3.14159265358979324 * 3000.0 / 180.0
const PI = 3.1415926535897932384626
const a = 6378245.0
const ee = 0.00669342162296594323

const MCBAND = [12890594.86, 8362377.87, 5591021, 3481989.83, 1678043.12, 0]
const LLBAND = [75, 60, 45, 30, 15, 0]

const MC2LL = [
  [1.410526172116255e-8, 0.00000898305509648872, -1.9939833816331, 200.9824383106796, -187.2403703815547,
    91.6087516669843, -23.38765649603339, 2.57121317296198, -0.03801003308653, 17337981.2],
  [-7.435856389565537e-9, 0.000008983055097726239, -0.78625201886289, 96.32687599759846, -1.85204757529826, -59.36935905485877, 47.40033549296737, -16.50741931063887, 2.28786674699375, 10260144.86],
  [-3.030883460898826e-8, 0.00000898305509983578, 0.30071316287616, 59.74293618442277, 7.357984074871, -25.38371002664745, 13.45380521110908, -3.29883767235584, 0.32710905363475, 6856817.37],
  [-1.981981304930552e-8, 0.000008983055099779535, 0.03278182852591, 40.31678527705744, 0.65659298677277, -4.44255534477492, 0.85341911805263, 0.12923347998204, -0.04625736007561, 4482777.06],
  [3.09191371068437e-9, 0.000008983055096812155, 0.00006995724062, 23.10934304144901, -0.00023663490511,
    -0.6321817810242, -0.00663494467273, 0.03430082397953, -0.00466043876332, 2555164.4],
  [2.890871144776878e-9, 0.000008983055095805407, -3.068298e-8, 7.47137025468032, -0.00000353937994,
    -0.02145144861037, -0.00001234426596, 0.00010322952773, -0.00000323890364, 826088.5]]

const LL2MC = [
  [-0.0015702102444, 111320.7020616939, 1704480524535203, -10338987376042340, 26112667856603880, -35149669176653700, 26595700718403920, -10725012454188240, 1800819912950474, 82.5],
  [0.0008277824516172526, 111320.7020463578, 647795574.6671607, -4082003173.641316, 10774905663.51142,
    -15171875531.51559, 12053065338.62167, -5124939663.577472, 913311935.9512032, 67.5],
  [0.00337398766765, 111320.7020202162, 4481351.045890365, -23393751.19931662, 79682215.47186455,
    -115964993.2797253, 97236711.15602145, -43661946.33752821, 8477230.501135234, 52.5],
  [0.00220636496208, 111320.7020209128, 51751.86112841131, 3796837.749470245, 992013.7397791013,
    -1221952.21711287, 1340652.697009075, -620943.6990984312, 144416.9293806241, 37.5],
  [-0.0003441963504368392, 111320.7020576856, 278.2353980772752, 2485758.690035394, 6070.750963243378,
    54821.18345352118, 9540.606633304236, -2710.55326746645, 1405.483844121726, 22.5],
  [-0.0003218135878613132, 111320.7020701615, 0.00369383431289, 823725.6402795718, 0.46104986909093,
    2351.343141331292, 1.58060784298199, 8.77738589078284, 0.37238884252424, 7.45]]

class CoordTransformUtils {
  // 百度坐标系（bd-09）转火星坐标系（gcj02）
  bd09togcj02(bdLng, bdLat) {
    const lng = +bdLng
    const lat = +bdLat

    const x = lng - 0.0065
    const y = lat - 0.006
    const z = Math.sqrt(x * x + y * y) - 0.00002 * Math.sin(y * xPI)

    const theta = Math.atan2(y, x) - 0.000003 * Math.cos(x * xPI)
    const gcjLng = z * Math.cos(theta)
    const gcjLat = z * Math.sin(theta)

    return [gcjLng, gcjLat]
  }

  // 火星坐标系（gcj02）转百度坐标系（bd-09）
  gcj02tobd09(gcjLng, gcjLat) {
    const lng = +gcjLng
    const lat = +gcjLat

    const z = Math.sqrt(lng * lng + lat * lat) + 0.00002 * Math.sin(lat * xPI)

    const theta = Math.atan2(lat, lng) + 0.000003 * Math.cos(lng * xPI)
    const bdLng = z * Math.cos(theta) + 0.0065
    const bdLat = z * Math.sin(theta) + 0.006

    return [bdLng, bdLat]
  }

  // 84坐标系（wgs84）转火星坐标系（gcj02）
  wgs84togcj02(wgsLng, wgsLat) {
    const lng = +wgsLng
    const lat = +wgsLat
    if (this.outOfChina(lng, lat)) {
      return [lng, lat]
    } else {
      let dlat = this.transformLat(lng - 105.0, lat - 35.0)
      let dlng = this.transformLng(lng - 105.0, lat - 35.0)

      const radlat = lat / 180.0 * PI
      let magic = Math.sin(radlat)
      magic = 1 - ee * magic * magic
      const sqrtmagic = Math.sqrt(magic)

      dlat = (dlat * 180.0) / ((a * (1 - ee)) / (magic * sqrtmagic) * PI)
      dlng = (dlng * 180.0) / (a / sqrtmagic * Math.cos(radlat) * PI)

      const mglat = lat + dlat
      const mglng = lng + dlng

      return [mglng, mglat]
    }
  }

  // 火星坐标系(gcj02) 转 84坐标系（wgs84）
  gcj02towgs84(gcjLng, gcjLat) {
    const lng = +gcjLng
    const lat = +gcjLat

    if (this.outOfChina(lng, lat)) {
      return [lng, lat]
    } else {
      let dlat = this.transformLat(lng - 105.0, lat - 35.0)
      let dlng = this.transformLng(lng - 105.0, lat - 35.0)

      const radlat = lat / 180.0 * PI
      let magic = Math.sin(radlat)
      magic = 1 - ee * magic * magic
      const sqrtmagic = Math.sqrt(magic)

      dlat = (dlat * 180.0) / ((a * (1 - ee)) / (magic * sqrtmagic) * PI)
      dlng = (dlng * 180.0) / (a / sqrtmagic * Math.cos(radlat) * PI)

      const mglat = lat + dlat
      const mglng = lng + dlng

      return [lng * 2 - mglng, lat * 2 - mglat]
    }
  }

  // 百度坐标系（bd09）转 84坐标系（wgs84）
  bd09towgs84(bdLng, bdLat) {
    const [gcjLng, gcjLat] = this.bd09togcj02(bdLng, bdLat)
    const wgsLngLat = this.gcj02towgs84(gcjLng, gcjLat)

    return wgsLngLat
  }

  // 84坐标系（wgs84）转 百度坐标系（bd09）
  wgs84tobd09(wgsLng, wgsLat) {
    const [gcjLng, gcjLat] = this.wgs84togcj02(wgsLng, wgsLat)
    const bd09LngLat = this.gcj02tobd09(gcjLng, gcjLat)

    return bd09LngLat
  }

  // 百度坐标系(bd09)转web墨卡托
  bd09tomercator(bdLng, bdLat) {
    let cE = ''
    const lng = this.getLoop(bdLng, -180, 180)
    const lat = this.getRange(bdLat, -74, 74)

    for (let i = 0; i < LLBAND.length; i++) {
      if (lat >= LLBAND[i]) {
        cE = LL2MC[i]
        break
      }
    }

    if (!cE) {
      for (let i = LLBAND.length - 1; i >= 0; i--) {
        if (lat <= -LLBAND[i]) {
          cE = LL2MC[i]
          break
        }
      }
    }

    return this.convert(lng, lat, cE)
  }

  // 墨卡托投影转百度坐标系（bd09）
  mercatortobd09(x, y) {
    let cF = null
    x = Math.abs(x)
    y = Math.abs(y)
    for (let cE = 0; cE < MCBAND.length; cE++) {
      if (y >= MCBAND[cE]) {
        cF = MC2LL[cE]
        break
      }
    }
    if (cF !== null) {
      const location = this.convert(x, y, cF)
      return location
    }
  }

  // 将外部传入的数据类型转为百度墨卡托投影下的数据类型  需要慢慢完善
  convertGeoIntoMap(data) {
    if (data instanceof ol.geom.Point) {
      const [lng, lat] = data.getCoordinates()
      const lnglat = this.bd09tomercator(lng, lat)
      data.setCoordinates(lnglat)
    } else if (data instanceof ol.geom.LineString) {
      const lineArr = []
      data.getCoordinates().forEach(linePoint => {
        const [lng, lat] = linePoint
        const lnglat = this.bd09tomercator(lng, lat)
        lineArr.push(lnglat)
      })
      data.setCoordinates(lineArr)
    }
  }

  // 将百度墨卡托转为百度坐标 需要慢慢完善
  convertGeoOutFromMap(data) {
    const newData = data
    if (newData instanceof ol.geom.Point) {
      const [x, y] = newData.getCoordinates()
      const lnglat = this.mercatortobd09(x, y)
      newData.setCoordinates(lnglat)
    } else if (newData instanceof ol.geom.LineString) {
      const lineArr = []
      newData.getCoordinates().forEach(linePoint => {
        const [x, y] = linePoint
        const lnglat = this.mercatortobd09(x, y)
        lineArr.push(lnglat)
      })
      newData.setCoordinates(lineArr)
    }
    return newData
  }

  // 墨卡托投影转百度坐标系（bd09）内置方法
  convert(x, y, cE) {
    let xTemp = cE[0] + cE[1] * Math.abs(x)
    const cC = Math.abs(y) / cE[9]
    let yTemp = cE[2] + cE[3] * cC + cE[4] * cC * cC + cE[5] * cC * cC * cC + cE[6] * cC * cC * cC * cC + cE[7] * cC * cC * cC * cC * cC + cE[8] * cC * cC * cC * cC * cC * cC
    xTemp *= (x < 0 ? -1 : 1)
    yTemp *= (y < 0 ? -1 : 1)

    return [xTemp, yTemp]
  }

  getLoop(lng, min, max) {
    while (lng > max) {
      lng -= max - min
    }
    while (lng < min) {
      lng += max - min
    }
    return lng
  }

  getRange(lat, min, max) {
    if (min != null) {
      lat = Math.max(lat, min)
    }
    if (max != null) {
      lat = Math.min(lat, max)
    }
    return lat
  }

  // 判断是否在国内 在国内返回true、不在国内返回false
  outOfChina(lng, lat) {
    lng = +lng
    lat = +lat

    // 纬度3.86~53.55,经度73.66~135.05
    return !(lng > 73.66 && lng < 135.05 && lat > 3.86 && lat < 53.55)
  }

  // 转换纬度坐标
  transformLat(lng, lat) {
    lng = +lng
    lat = +lat

    let ret = -100.0 + 2.0 * lng + 3.0 * lat + 0.2 * lat * lat + 0.1 * lng * lat + 0.2 * Math.sqrt(Math.abs(lng))
    ret += (20.0 * Math.sin(6.0 * lng * PI) + 20.0 * Math.sin(2.0 * lng * PI)) * 2.0 / 3.0
    ret += (20.0 * Math.sin(lat * PI) + 40.0 * Math.sin(lat / 3.0 * PI)) * 2.0 / 3.0
    ret += (160.0 * Math.sin(lat / 12.0 * PI) + 320 * Math.sin(lat * PI / 30.0)) * 2.0 / 3.0

    return ret
  }

  // 转换经度坐标
  transformLng(lng, lat) {
    lng = +lng
    lat = +lat

    let ret = 300.0 + lng + 2.0 * lat + 0.1 * lng * lng + 0.1 * lng * lat + 0.1 * Math.sqrt(Math.abs(lng))
    ret += (20.0 * Math.sin(6.0 * lng * PI) + 20.0 * Math.sin(2.0 * lng * PI)) * 2.0 / 3.0
    ret += (20.0 * Math.sin(lng * PI) + 40.0 * Math.sin(lng / 3.0 * PI)) * 2.0 / 3.0
    ret += (150.0 * Math.sin(lng / 12.0 * PI) + 300.0 * Math.sin(lng / 30.0 * PI)) * 2.0 / 3.0

    return ret
  }
}

export default CoordTransformUtils
